package events;

import java.net.UnknownHostException;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Iterator;

import javax.swing.event.EventListenerList;
import packets.OSPFAcknowledgement;
import packets.OSPFDescription;
import packets.OSPFHello;
import packets.OSPFRequest;
import packets.OSPFUpdate;
import dataStructures.*;
import router.Interface;

/**
 * Diese Klasse repräsentiert die Ablaufsteuerung des OSPF Protokolls. Hier werden alle möglichen EventListener registriert. 
 * Tritt ein solches Event auf, wird der EventManager benachrichtigt. Der EventManager ruft die Listener auf, die für ein bestimmtes Ereignis (@see EventObject)
 * registriert wurden. Singleton.
 * @author pascal
 *
 */
public class EventManager {
	
	private static EventManager instance = null;
	private EventListenerList listeners = new EventListenerList();
	
	/**
	 * Im Konstruktor werden alle Listener registriert. 
	 */
	private EventManager(){
		
		//Listener die auf eingehende Pakete reagieren
		listeners.add(ReceiveHelloListener.class, new ReceiveHelloListener());
		listeners.add(ReceiveAcknowledgementListener.class, new ReceiveAcknowledgementListener());
		listeners.add(ReceiveDescriptionsListener.class, new ReceiveDescriptionsListener());
		listeners.add(ReceiveRequestListener.class, new ReceiveRequestListener());
		listeners.add(ReceiveUpdateListener.class, new ReceiveUpdateListener());
		
		//Listener die auf abgelaufene Timer reagieren
		listeners.add(InactivityTimerListener.class, new InactivityTimerListener());
		listeners.add(HelloIntervalListener.class, new HelloIntervalListener());
		
		//Listener für Zustandsänderungen
		listeners.add(NeighborStateChangedListener.class, new NeighborStateChangedListener());
		listeners.add(InterfaceStateChangedListener.class, new InterfaceStateChangedListener());
	}
	
	public static EventManager getInstance(){
		if(instance == null){
			instance = new EventManager();
			
		}
		
		return instance;
	}
	
	
	/**
	 * Diese Methode benachrichtigt alle Listener, die auf eingehende Pakete reagieren.
	 * @param event das entsprechende Ereignis.
	 * @throws UnknownHostException
	 */
	public synchronized void notifyPacketReceived(ReceivedEvent event) throws UnknownHostException{
		
		if(event.getProtocolPacket() instanceof OSPFHello){
			for(EventListener l : listeners.getListeners(ReceiveHelloListener.class)){
				((ReceivePacketListener)l).processPacket(event);
			}
		}
		
		else if(event.getProtocolPacket() instanceof OSPFAcknowledgement){
			for(EventListener l: listeners.getListeners(ReceiveRequestListener.class)){
				((ReceivePacketListener)l).processPacket(event);
			}
		}
		
		else if(event.getProtocolPacket() instanceof OSPFAcknowledgement){
			for(EventListener l: listeners.getListeners(ReceiveAcknowledgementListener.class)){
				((ReceivePacketListener)l).processPacket(event);
			}
		}
		
		else if(event.getProtocolPacket() instanceof OSPFDescription){
			for(EventListener l: listeners.getListeners(ReceiveDescriptionsListener.class)){
				((ReceivePacketListener)l).processPacket(event);
			}
		}
		
		else if(event.getProtocolPacket() instanceof OSPFRequest){
			for(EventListener l: listeners.getListeners(ReceiveRequestListener.class)){
				((ReceivePacketListener)l).processPacket(event);
			}
		}
		
		else if(event.getProtocolPacket() instanceof OSPFUpdate){
			for(EventListener l: listeners.getListeners(ReceiveUpdateListener.class)){
				((ReceivePacketListener)l).processPacket(event);
			}
		}
		
		else System.out.println("Unbekanntes PacketReceived Event!");
	}
	
	/**
	 * Diese Methode benachrichtigt alle Listener, die auf ablaufende Timer reagieren.
	 * @param event das entsprechende Ereignis.
	 */
	public synchronized void notifyTimerExpired(EventObject event){
		
		if(event instanceof RouterDeadEvent){
			for(EventListener l: listeners.getListeners(InactivityTimerListener.class)){
				((InactivityTimerListener)l).processTimerExpired((NeighborDataStructure) event.getSource());
			}
			
		}
		
		else if(event instanceof SendHelloEvent){
			for(EventListener l: listeners.getListeners(HelloIntervalListener.class)){
				((HelloIntervalListener)l).processTimerExpired((Interface)event.getSource());
			}
		}
		
		else if(event instanceof WaitTimerExpiredEvent){
			for(EventListener l: listeners.getListeners(WaitTimerExpiredListener.class)){
				((WaitTimerExpiredListener)l).processTimerExpired((Interface)event.getSource());
			}
		}
		
	}
	
	/**
	 * Diese Methode benachrichtigt Listener, die auf Zustandsänderungen des Routerinterfaces oder der Nachbarn reagieren.
	 * @param event das entsprechende Ereignis.
	 */
	public synchronized void notifyStateChanged(EventObject event){
		if(event.getSource() instanceof Interface){
			for(EventListener l: listeners.getListeners(InterfaceStateChangedListener.class)){
				((InterfaceStateChangedListener)l).processStateChange((Interface) event.getSource());
			}
		}
		else if(event.getSource() instanceof NeighborDataStructure){
			for(EventListener l: listeners.getListeners(NeighborStateChangedListener.class)){
				((NeighborStateChangedListener)l).processStateChange((NeighborDataStructure) event.getSource());
			}
		}
	}
	

	
	
	
	

}
